package com.example.utils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 高并发环境下用于生成唯一流水编号的工具类
 */
public class SerialNumberGenerator {

    // 常量配置
    private static final int SEQUENCE_BITS = 12;       // 序列号位数
    private static final int MACHINE_BITS = 10;        // 机器ID位数
    private static final int MAX_SEQUENCE = ~(-1 << SEQUENCE_BITS); // 最大序列号值
    private static final long MACHINE_ID = 1L << SEQUENCE_BITS;     // 机器ID左移后值

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");

    // 状态变量
    private static long lastTimestamp = -1L;
    private static AtomicInteger sequence = new AtomicInteger(0);

    /**
     * 生成下一个序列号（返回30位字符串）
     * 格式：yyyyMMddHHmmss + 机器ID（5位） + 序列号（8位）
     */
    public static String generateNextNumber() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨");
        }

        if (timestamp == lastTimestamp) {
            int currentSequence = sequence.incrementAndGet();
            if (currentSequence > MAX_SEQUENCE) {
                timestamp = tilNextMillis(lastTimestamp);
                sequence.set(0);
            }
        } else {
            sequence.set(0);
        }

        lastTimestamp = timestamp;

        String timeStr = DATE_FORMAT.format(new Date(timestamp));
        String machineIdStr = String.format("%05d", MACHINE_ID >>> SEQUENCE_BITS); // 提取机器ID部分
        String seqStr = String.format("%08d", sequence.get());

        return timeStr + machineIdStr + seqStr;
    }

    /**
     * 等待直到获得一个新的毫秒时间戳
     */
    private static long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 主方法测试
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(generateNextNumber());
            }).start();
        }
    }
}

